package com.maomao.test;

import com.lan.source.tree.Tree;
import com.lan.tools.javac.api.JavacTrees;
import com.lan.tools.javac.code.Flags;
import com.lan.tools.javac.processing.JavacProcessingEnvironment;
import com.lan.tools.javac.tree.JCTree;
import com.lan.tools.javac.tree.TreeMaker;
import com.lan.tools.javac.tree.TreeTranslator;
import com.lan.tools.javac.util.*;

import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic;
import java.util.Set;

@SupportedAnnotationTypes({"com.maomao.test.Getter"})
@SupportedSourceVersion(SourceVersion.RELEASE_14)
public class GetSetProcessor extends AbstractProcessor {

    private Messager messager;
    private JavacTrees trees;
    private TreeMaker treeMaker;
    private Names names;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        this.messager = processingEnv.getMessager();
        this.trees = JavacTrees.instance(processingEnv);
        Context context = ((JavacProcessingEnvironment) processingEnv).getContext();
        this.treeMaker = TreeMaker.instance(context);
        this.names = Names.instance(context);
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Getter.class);
        TreeTranslator treeTranslator = new TreeTranslator() {
            @Override
            public void visitClassDef(JCTree.JCClassDecl jcClassDecl) {
                List<JCTree.JCVariableDecl> jcVariableDeclList = List.nil();
                for (JCTree tree : jcClassDecl.defs) {
                    if (tree.getKind().equals(Tree.Kind.VARIABLE)) {
                        JCTree.JCVariableDecl jcVariableDecl = (JCTree.JCVariableDecl) tree;
                        jcVariableDeclList = jcVariableDeclList.append(jcVariableDecl);
                    }
                }

                for (JCTree.JCVariableDecl jcVariableDecl : jcVariableDeclList) {
                    messager.printMessage(Diagnostic.Kind.NOTE, jcVariableDecl.getName() + "has been processed");
                    jcClassDecl.defs = jcClassDecl.defs.prepend(makeGetterMethodDecl(jcVariableDecl));
                    super.visitClassDef(jcClassDecl);
                }
            }
        };
        for (Element element: elements){
            JCTree jcTree = trees.getTree(element);
            jcTree.accept(treeTranslator);
        }
        return true;
    }

    private JCTree.JCMethodDecl makeGetterMethodDecl(JCTree.JCVariableDecl jcVariableDecl) {
        ListBuffer<JCTree.JCStatement> statements = new ListBuffer<>();
        JCTree.JCIdent jcIdent = treeMaker.Ident(names.fromString("this"));
        JCTree.JCFieldAccess jcFieldAccess = treeMaker.Select(jcIdent, jcVariableDecl.getName());
        JCTree.JCReturn jcReturn = treeMaker.Return(jcFieldAccess);
        statements.append(jcReturn);
        JCTree.JCBlock body = treeMaker.Block(0, statements.toList());
        return treeMaker.MethodDef(
                treeMaker.Modifiers(Flags.PUBLIC),
                getNewMethodName(jcVariableDecl.getName()),
                jcVariableDecl.vartype,
                List.nil(),
                List.nil(),
                List.nil(),
                body,
                null);
    }

    private Name getNewMethodName(Name name) {
        String s = name.toString();
        String mn = "get" + s.substring(0, 1).toUpperCase() + s.substring(1, name.length());
        return names.fromString(mn);
    }
}
